package xyz.lwm.lazycat.vertx;

import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.RoutingContext;
import xyz.lwm.lazycat.utility.IoUtil;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.StringUtil;
import xyz.lwm.lazycat.utility.exception.IoRuntimeException;
import xyz.lwm.lazycat.web.handle.ContentType;
import xyz.lwm.lazycat.web.handle.Header;
import xyz.lwm.lazycat.web.handle.HttpResponse;
import xyz.lwm.lazycat.web.handle.HttpStatus;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * vertx http response
 *
 * @author lwm
 */
class VertxHttpResponse implements HttpResponse {

    private final Buffer bodyBuffer = Buffer.buffer();

    private final HttpServerResponse response;

    private Map<String, List<String>> headersCache;

    private Charset charset;
    /**
     * 是否已经发送了Http状态码，如果没有，提前写出状态码
     */
    private boolean isSendCode;

    VertxHttpResponse(RoutingContext rctx) {
        this.response = rctx.response();
    }

    @Override
    public void setStatus(int httpStatusCode) {
        setStatus(httpStatusCode, 0);
    }

    @Override
    public void setStatus(int httpStatusCode, long bodyLength) {
        if (this.isSendCode) {
            throw new IoRuntimeException("Http status code has been send!");
        }

        response.setStatusCode(httpStatusCode);
        response.putHeader(Header.CONTENT_LENGTH.name(), String.valueOf(bodyLength));

        this.isSendCode = true;
    }

    @Override
    public boolean hasSetStatus() {
        return isSendCode;
    }

    @Override
    public Map<String, List<String>> getHeaders() {
        if (headersCache == null) {
            headersCache = new HashMap<>();
            MultiMap headers = response.headers();
            headers.forEach(e ->
                    headersCache.computeIfAbsent(e.getKey(),
                            k -> new ArrayList<>()).add(e.getValue()));
        }
        return headersCache;
    }

    @Override
    public void addHeader(String header, String value) {
        response.headers().add(header, value);
    }

    @Override
    public void setHeader(String header, String value) {
        response.headers().set(header, value);
    }

    @Override
    public void setHeader(Header header, String value) {
        response.headers().set(header.getValue(), value);
    }

    @Override
    public void setHeaders(Map<String, List<String>> headers) {
        headers.forEach((k, v) -> response.headers().set(k, v));
    }

    @Override
    public void setContentType(String contentType) {
        if (null != contentType && null != this.charset) {
            if (!contentType.contains(";charset=")) {
                contentType = ContentType.build(contentType, this.charset);
            }
        }
        setHeader(Header.CONTENT_TYPE, contentType);
    }

    @Override
    public void setContentLength(long contentLength) {
        setHeader(Header.CONTENT_LENGTH, String.valueOf(contentLength));
    }

    @Override
    public void setCharset(Charset charset) {
        this.charset = charset;
    }

    @Override
    public void setAttr(String key, String value) {
        throw new IoRuntimeException("Not support set attr in vertx!");
    }

    @Override
    public OutputStream getOut() {
        return new OutputStream() {
            @Override
            public void write(int b) {
                bodyBuffer.appendByte((byte) b);
            }
            @Override
            public void write(byte[] b) {
                bodyBuffer.appendBytes(b);
            }
            @Override
            public void write(byte[] b, int off, int len) {
                bodyBuffer.appendBytes(b, off, len);
            }
        };
    }

    @Override
    public void write(String data) {
        Charset charset = It.of(this.charset).getOr(Charset.defaultCharset());
        if (StringUtil.isNotBlank(data)) {
            write(data.getBytes(charset));
        }
    }

    @Override
    public void write(byte[] data) {
        final ByteArrayInputStream in = new ByteArrayInputStream(data);
        write(in, in.available());
    }

    @Override
    public void write(InputStream in) {
        write(in, 0);
    }

    @Override
    public void write(InputStream in, int length) {
        if (!isSendCode) {
            setStatus(HttpStatus.HTTP_OK, Math.max(0, length));
        }

        OutputStream out = getOut();
        IoUtil.copy(in, out, true);
        setHeader(Header.CONTENT_LENGTH, String.valueOf(bodyBuffer.length()));
        response.write(bodyBuffer);
    }

    @Override
    public void write(InputStream in, int length, String contentType, String fileName) {
        Charset charset = It.of(this.charset).getOr(Charset.defaultCharset());
        if (!contentType.startsWith("text/")) {
            // 非文本类型数据直接走下载
            String str = StringUtil.format("attachment;filename={}",
                    VertxParamUtil.encode(fileName, charset));
            setHeader(Header.CONTENT_DISPOSITION, str);
        }
        write(in, length);
    }

}
